package com.demo.statemachine.d10;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.action.StateDoActionPolicy;
import org.springframework.statemachine.config.EnableStateMachine;
import org.springframework.statemachine.config.StateMachineBuilder;
import org.springframework.statemachine.config.configurers.StateConfigurer;

import java.util.EnumSet;
import java.util.concurrent.TimeUnit;

@Configuration
@EnableStateMachine
public class SwapStateMachineBuilder {
    @Autowired
    private BeanFactory beanFactory;
    private final static String MACHINEID = "swapMachine";

    public StateMachine<SwapStates, SwapEvents> build() throws Exception {
        StateMachineBuilder.Builder<SwapStates, SwapEvents> builder = StateMachineBuilder.builder();
        builder.configureConfiguration().withConfiguration()
                .machineId(MACHINEID)
                .beanFactory(beanFactory)
                .listener(new SwapStateMachineListener())
                .stateDoActionPolicy(StateDoActionPolicy.TIMEOUT_CANCEL)
                .stateDoActionPolicyTimeout(2, TimeUnit.SECONDS)
                ;
//        builder.configureModel()
//                ;
        builder.configureStates().withStates()
                .initial(SwapStates.IDLE,SwapActions.IDLE_ENTRY_ACTION)
                .state(SwapStates.BUSY,SwapActions.BUSY_ENTRY_ACTION,null)
                .state(SwapStates.SUSPEND,SwapActions.BUSY_ENTRY_ACTION,null)

                .and().withStates()
                    .parent(SwapStates.BUSY)
                    .initial(SwapStates.BUSY_START)
                    .state(SwapStates.BUSY_START)

                    .fork(SwapStates.DATA_FORK)
                    .state(SwapStates.DATA_TASK,SwapActions.DATA_TASK_ENTRY_ACTION,null)
                    .join(SwapStates.DATA_JOIN)
                    .choice(SwapStates.DATA_CHOICE)

                    .fork(SwapStates.ENV_FORK)
                    .state(SwapStates.ENV_TASK,SwapActions.ENV_TASK_ENTRY_ACTION,null)
                    .join(SwapStates.ENV_JOIN)
                    .choice(SwapStates.ENV_CHOICE)

                    .fork(SwapStates.PRESET_FORK)
                    .state(SwapStates.PRESET_TASK,SwapActions.PRESET_TASK_ENTRY_ACTION,null)
                    .join(SwapStates.PRESET_JOIN)
                    .choice(SwapStates.PRESET_CHOICE)

                    .fork(SwapStates.SWAP_FORK)
                    .state(SwapStates.SWAP_TASK,SwapActions.SWAP_TASK_ENTRY_ACTION,null)
                    .join(SwapStates.SWAP_JOIN)
                    .choice(SwapStates.SWAP_CHOICE)

//                    .choice(SwapStates.AGAIN_SWAP_CHOICE)

                    .fork(SwapStates.END_FORK)
                    .state(SwapStates.END_TASK,SwapActions.END_TASK_ENTRY_ACTION,null)
                    .join(SwapStates.END_JOIN)
                    .choice(SwapStates.END_CHOICE)

                    .fork(SwapStates.RESET_FORK)
                    .state(SwapStates.RESET_TASK,SwapActions.RESET_TASK_ENTRY_ACTION,null)
                    .join(SwapStates.RESET_JOIN)
                    .choice(SwapStates.RESET_CHOICE)


////************************ data_check branch **********************************
//                  data_check first parallel branch
                    .and().withStates()
                        .parent(SwapStates.DATA_TASK)
                        .initial(SwapStates.DATA_VEHICLE_PLATE_CHECK,SwapActions.DATA_VEHICLE_PLATE_CHECK_ENTRY_ACTION)
                        .state(SwapStates.DATA_VEHICLE_PLATE_CHECK,SwapActions.DATA_VEHICLE_PLATE_CHECK_ENTRY_ACTION,null)
                        .state(SwapStates.DATA_CLOUD_AUTH,SwapActions.DATA_CLOUD_AUTH_ENTRY_ACTION,null)
                        .state(SwapStates.DATA_VEHICLE_CONNECTION,SwapActions.DATA_VEHICLE_CONNECTION_ENTRY_ACTION,null)
                        .end(SwapStates.DATA_VEHICLE_CONNECTION)
//                  data_check second parallel branch
                    .and().withStates()
                        .parent(SwapStates.DATA_TASK)
                        .initial(SwapStates.DATA_V_IN_PLACE,SwapActions.DATA_V_IN_PLACE_ENTRY_ACTION)
                        .state(SwapStates.DATA_V_IN_PLACE,SwapActions.DATA_V_IN_PLACE_ENTRY_ACTION,null)
                        .state(SwapStates.DATA_VEHICLE_IN_PLACE,SwapActions.DATA_VEHICLE_IN_PLACE_ENTRY_ACTION,null)
                        .state(SwapStates.DATA_VEHICLE_FREEDOM,SwapActions.DATA_VEHICLE_FREEDOM_ENTRY_ACTION,null)
                        .end(SwapStates.DATA_VEHICLE_FREEDOM)
//                  data_check third parallel branch
//                    .and().withStates()
//                        .parent(SwapStates.DATA_TASK)
//                        .initial(SwapStates.DATA_SWAP_TYPE_CHOICE)
//                        .state(SwapStates.DATA_SWAP_TYPE_CHOICE,SwapActions.DATA_SWAP_TYPE_CHOICE_ENTRY_ACTION,null)
//                        .state(SwapStates.DATA_FEEL_SWAP,SwapActions.DATA_FEEL_SWAP_ENTRY_ACTION,null)
//                        .state(SwapStates.DATA_NO_FEEL_SWAP,SwapActions.DATA_NO_FEEL_SWAP_ENTRY_ACTION,null)
//                        .state(SwapStates.DATA_SCAN_CODE,SwapActions.DATA_SCAN_CODE_ENTRY_ACTION,null)
//************************ env_check branch **********************************
//                  env_check first parallel branch
                    .and().withStates()
                        .parent(SwapStates.ENV_TASK)
                        .initial(SwapStates.ENV_FETCH_STRATEGY,SwapActions.ENV_FETCH_STRATEGY_ENTRY_ACTION)
                        .state(SwapStates.ENV_FETCH_STRATEGY)
                        .state(SwapStates.ENV_FETCH_STRATEGY_RESULT)
                        .end(SwapStates.ENV_FETCH_STRATEGY_RESULT)
//                  env_check second parallel branch
                    .and().withStates()
                        .parent(SwapStates.ENV_TASK)
                        .initial(SwapStates.ENV_CAR_MACHINE_CONFIRM,SwapActions.ENV_CAR_MACHINE_CONFIRM_ENTRY_ACTION)
                        .state(SwapStates.ENV_CAR_MACHINE_CONFIRM)
                        .state(SwapStates.ENV_CAR_MACHINE_CONFIRM_RESULT)
                        .end(SwapStates.ENV_CAR_MACHINE_CONFIRM_RESULT)
//                  env_check third parallel branch
                    .and().withStates()
                        .parent(SwapStates.ENV_TASK)
                        .initial(SwapStates.ENV_PLC_CONFIRM,SwapActions.ENV_PLC_CONFIRM_ENTRY_ACTION)
                        .state(SwapStates.ENV_PLC_CONFIRM)
                        .state(SwapStates.ENV_PLC_CONFIRM_RESULT)
                        .end(SwapStates.ENV_PLC_CONFIRM_RESULT)
//************************ preset_task branch **********************************
//                  preset_task first parallel branch
                    .and().withStates()
                        .parent(SwapStates.PRESET_TASK)
                        .initial(SwapStates.PRESET_TASK1,SwapActions.PRESET_TASK1_ENTRY_ACTION)
                        .state(SwapStates.PRESET_TASK1)
                        .state(SwapStates.PRESET_TASK1_RESULT)
                        .end(SwapStates.PRESET_TASK1_RESULT)
//************************ swap_task branch **********************************
//                  swap_task first parallel branch
                    .and().withStates()
                        .parent(SwapStates.SWAP_TASK)
                        .initial(SwapStates.SWAP_START,SwapActions.SWAP_START_ENTRY_ACTION)
                        .state(SwapStates.SWAP_START,SwapActions.SWAP_START_ENTRY_ACTION,null)
                        .state(SwapStates.SWAP_UNINSTALL)
                        .state(SwapStates.SWAP_TRANSPORT)
                        .state(SwapStates.SWAP_INSTALL)
                        .state(SwapStates.SWAP_VEHICLE_SELF_CHECK)
                        .state(SwapStates.SWAP_FINISH)
                        .end(SwapStates.SWAP_FINISH)
//************************ end_task branch **********************************
//                  end_task first parallel branch
                    .and().withStates()
                        .parent(SwapStates.END_TASK)
                        .initial(SwapStates.END_AFTER_ROAD_GATE_STARTUP,SwapActions.END_AFTER_ROAD_GATE_STARTUP_ENTRY_ACTION)
                        .state(SwapStates.END_VEHICLE_OFF_PLACE,SwapActions.END_VEHICLE_OFF_PLACE_ENTRY_ACTION,null)
                        .state(SwapStates.END_AFTER_ROAD_GATE_SHUTDOWN,SwapActions.END_AFTER_ROAD_GATE_SHUTDOWN_ENTRY_ACTION,null)
                        .state(SwapStates.END_SWAP_END,SwapActions.END_SWAP_END_ENTRY_ACTION,null)
                        .end(SwapStates.END_SWAP_END)
//************************ reset_task branch **********************************
//                  reset_task first parallel branch
                    .and().withStates()
                        .parent(SwapStates.RESET_TASK)
                        .initial(SwapStates.RESET_CAR_MACHINE,SwapActions.RESET_CAR_MACHINE_ENTRY_ACTION)
                        .state(SwapStates.RESET_CAR_MACHINE)
                        .state(SwapStates.RESET_CAR_MACHINE_RESULT)
                        .end(SwapStates.RESET_CAR_MACHINE_RESULT)
//                  reset_task second parallel branch
                    .and().withStates()
                        .parent(SwapStates.RESET_TASK)
                        .initial(SwapStates.RESET_PLC,SwapActions.RESET_PLC_ENTRY_ACTION)
                        .state(SwapStates.RESET_PLC)
                        .state(SwapStates.RESET_PLC_RESULT)
                        .end(SwapStates.RESET_PLC_RESULT)
//                  reset_task third parallel branch
                    .and().withStates()
                        .parent(SwapStates.RESET_TASK)
                        .initial(SwapStates.RESET_RBC,SwapActions.RESET_RBC_ENTRY_ACTION)
                        .state(SwapStates.RESET_RBC)
                        .state(SwapStates.RESET_RBC_RESULT)
                        .end(SwapStates.RESET_RBC_RESULT)
//***********************************************************************************************
                .state(SwapStates.FAULT,SwapActions.FAULT_ENTRY_ACTION,null)
                .history(SwapStates.HISTORY, StateConfigurer.History.SHALLOW)
        ;


        builder.configureTransitions()

                .withExternal()
                    .source(SwapStates.IDLE)
                    .target(SwapStates.BUSY)
                    .event(SwapEvents.IDLE_BUSY)
                .and().withExternal()
                    .source(SwapStates.BUSY)
                    .target(SwapStates.IDLE)
                    .event(SwapEvents.BUSY_IDLE)
                .and().withExternal()
                    .source(SwapStates.BUSY)
                    .target(SwapStates.SUSPEND)
                    .event(SwapEvents.BUSY_SUSPEND)
                .and().withExternal()
                    .source(SwapStates.SUSPEND)
                    .target(SwapStates.BUSY)
                    .event(SwapEvents.BUSY_SUSPEND)

                .and().withExternal()
                    .source(SwapStates.BUSY_START)
                    .target(SwapStates.DATA_FORK)
                .and().withFork()
                    .source(SwapStates.DATA_FORK)
                    .target(SwapStates.DATA_TASK)
                .and().withExternal()
                    .source(SwapStates.DATA_VEHICLE_PLATE_CHECK)
                    .target(SwapStates.DATA_CLOUD_AUTH)
                .and().withExternal()
                    .source(SwapStates.DATA_CLOUD_AUTH)
                    .target(SwapStates.DATA_VEHICLE_CONNECTION)
                .and().withExternal()
                    .source(SwapStates.DATA_V_IN_PLACE)
                    .target(SwapStates.DATA_VEHICLE_IN_PLACE)
                .and().withExternal()
                    .source(SwapStates.DATA_VEHICLE_IN_PLACE)
                    .target(SwapStates.DATA_VEHICLE_FREEDOM)
//                .and().withChoice()
//                    .source(SwapStates.DATA_SWAP_TYPE_CHOICE)
//                    .first(SwapStates.DATA_FEEL_SWAP,SwapGuards.DATA_FEEL_SWAP_GUARD)
//                    .last(SwapStates.DATA_NO_FEEL_SWAP)
//                .and().withExternal()
//                    .source(SwapStates.DATA_FEEL_SWAP)
//                    .target(SwapStates.DATA_SCAN_CODE)
                .and().withJoin()
                    .source(SwapStates.DATA_TASK)
                    .target(SwapStates.DATA_JOIN)
                .and().withExternal()
                    .source(SwapStates.DATA_JOIN)
                    .target(SwapStates.DATA_CHOICE)
                .and().withChoice()
                    .source(SwapStates.DATA_CHOICE)
                    .first(SwapStates.ENV_FORK,SwapGuards.DATA_CHOICE_GUARD)
                    .last(SwapStates.FAULT)


                .and().withFork()
                    .source(SwapStates.ENV_FORK)
                    .target(SwapStates.ENV_TASK)
                .and().withExternal()
                    .source(SwapStates.ENV_FETCH_STRATEGY)
                    .target(SwapStates.ENV_FETCH_STRATEGY_RESULT)
                .and().withExternal()
                    .source(SwapStates.ENV_CAR_MACHINE_CONFIRM)
                    .target(SwapStates.ENV_CAR_MACHINE_CONFIRM_RESULT)
                .and().withExternal()
                    .source(SwapStates.ENV_PLC_CONFIRM)
                    .target(SwapStates.ENV_PLC_CONFIRM_RESULT)
                .and().withJoin()
                    .source(SwapStates.ENV_TASK)
                    .target(SwapStates.ENV_JOIN)
                .and().withExternal()
                    .source(SwapStates.ENV_JOIN)
                    .target(SwapStates.ENV_CHOICE)
                .and().withChoice()
                    .source(SwapStates.ENV_CHOICE)
                    .first(SwapStates.PRESET_FORK,SwapGuards.ENV_CHOICE_GUARD)
                    .last(SwapStates.FAULT)


                .and().withFork()
                    .source(SwapStates.PRESET_FORK)
                    .target(SwapStates.PRESET_TASK)
                .and().withExternal()
                    .source(SwapStates.PRESET_TASK1)
                    .target(SwapStates.PRESET_TASK1_RESULT)
                .and().withJoin()
                    .source(SwapStates.PRESET_TASK)
                    .target(SwapStates.PRESET_JOIN)
                .and().withExternal()
                    .source(SwapStates.PRESET_JOIN)
                    .target(SwapStates.PRESET_CHOICE)
                .and().withChoice()
                    .source(SwapStates.PRESET_CHOICE)
                    .first(SwapStates.SWAP_FORK,SwapGuards.PRESET_CHOICE_GUARD)
                    .last(SwapStates.FAULT)


                .and().withFork()
                    .source(SwapStates.SWAP_FORK)
                    .target(SwapStates.SWAP_TASK)
                .and().withExternal()
                    .source(SwapStates.SWAP_START)
                    .target(SwapStates.SWAP_UNINSTALL)
                .and().withExternal()
                    .source(SwapStates.SWAP_UNINSTALL)
                    .target(SwapStates.SWAP_TRANSPORT)
                .and().withExternal()
                    .source(SwapStates.SWAP_TRANSPORT)
                    .target(SwapStates.SWAP_INSTALL)
                .and().withExternal()
                    .source(SwapStates.SWAP_INSTALL)
                    .target(SwapStates.SWAP_VEHICLE_SELF_CHECK)
                .and().withExternal()
                    .source(SwapStates.SWAP_VEHICLE_SELF_CHECK)
                    .target(SwapStates.SWAP_FINISH)
                .and().withJoin()
                    .source(SwapStates.SWAP_TASK)
                    .target(SwapStates.SWAP_JOIN)
                .and().withExternal()
                    .source(SwapStates.SWAP_JOIN)
                    .target(SwapStates.SWAP_CHOICE)
                .and().withChoice()
                    .source(SwapStates.SWAP_CHOICE)
                    .first(SwapStates.END_FORK,SwapGuards.SWAP_CHOICE_GUARD)
                    .last(SwapStates.FAULT)


//                .and().withChoice()
//                    .source(SwapStates.AGAIN_SWAP_CHOICE)
//                    .first(SwapStates.ENV_FORK,SwapGuards.AGAIN_SWAP_CHOICE_GUARD)
//                    .last(SwapStates.END_FORK)


                .and().withFork()
                    .source(SwapStates.END_FORK)
                    .target(SwapStates.END_TASK)
                .and().withExternal()
                    .source(SwapStates.END_AFTER_ROAD_GATE_STARTUP)
                    .target(SwapStates.END_VEHICLE_OFF_PLACE)
                .and().withExternal()
                    .source(SwapStates.END_VEHICLE_OFF_PLACE)
                    .target(SwapStates.END_AFTER_ROAD_GATE_SHUTDOWN)
                .and().withExternal()
                    .source(SwapStates.END_AFTER_ROAD_GATE_SHUTDOWN)
                    .target(SwapStates.END_SWAP_END)
                .and().withJoin()
                    .source(SwapStates.END_TASK)
                    .target(SwapStates.END_JOIN)
                .and().withExternal()
                    .source(SwapStates.END_JOIN)
                    .target(SwapStates.END_CHOICE)
                .and().withChoice()
                    .source(SwapStates.END_CHOICE)
                    .first(SwapStates.RESET_FORK,SwapGuards.END_CHOICE_GUARD)
                    .last(SwapStates.FAULT)


                .and().withFork()
                    .source(SwapStates.RESET_FORK)
                    .target(SwapStates.RESET_TASK)
                .and().withExternal()
                    .source(SwapStates.RESET_CAR_MACHINE)
                    .target(SwapStates.RESET_CAR_MACHINE_RESULT)
                .and().withExternal()
                    .source(SwapStates.RESET_PLC)
                    .target(SwapStates.RESET_PLC_RESULT)
                .and().withExternal()
                    .source(SwapStates.RESET_RBC)
                    .target(SwapStates.RESET_RBC_RESULT)
                .and().withJoin()
                    .source(SwapStates.RESET_TASK)
                    .target(SwapStates.RESET_JOIN)
                .and().withExternal()
                    .source(SwapStates.RESET_JOIN)
                    .target(SwapStates.RESET_CHOICE)
                .and().withChoice()
                    .source(SwapStates.RESET_CHOICE)
                    .last(SwapStates.IDLE)

                .and().withExternal()
                    .source(SwapStates.FAULT)
                    .target(SwapStates.RESET_FORK)
                ;

        return builder.build();
    }
}
